package com.tom.user.controller;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.tom.common.exception.CustomException;
import com.tom.common.exception.CustomUnauthorizedException;
import com.tom.common.model.ResponseResult;
import com.tom.common.utils.AesCipherUtil;
import com.tom.common.utils.AvatarUtil;
import com.tom.common.utils.PasswordEncrypterUtil;
import com.tom.user.entity.User;
import com.tom.user.model.Constant;
import com.tom.user.model.UserResultModel;
import com.tom.user.model.UserValidModel;
import com.tom.user.service.GeetestVerifyService;
import com.tom.user.service.UserService;
import com.tom.user.utils.JedisUtil;
import com.tom.user.utils.JwtUtil;
import com.tom.user.utils.UserUtil;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresGuest;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Date;

/**
 * @author longjiang
 * @date 2020-03-26 11:25 上午
 * @description 用户
 **/

@Slf4j
@Api(tags = "用户相关")
@RestController
@RequestMapping("user")
public class UserController {
    @Resource
    private PasswordEncrypterUtil passwordEncrypterUtil;


    @Resource
    private UserUtil userUtil;

    /**
     * 随机数生成器
     */
    private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();


    /**
     * token过期时间
     */
    @Value("${token.refreshTokenExpireTime}")
    private String refreshTokenExpireTime;


    /**
     * 使用构造器方式注入service
     */
    private final UserService userService;

    private final GeetestVerifyService geetestVerifyService;

    @Autowired
    public UserController(UserService userService,GeetestVerifyService geetestVerifyService) {
        this.userService = userService;
        this.geetestVerifyService=geetestVerifyService;
    }


    /**
     * 登录  手机号+密码方式
     * @param userValidModel
     * @param response
     * @param response
     * @return
     */
    @ApiOperation(value = "登录", notes = "用户手机号/密码登录")
    @PostMapping("/login")
    public ResponseResult login(@RequestBody UserValidModel userValidModel, HttpServletResponse response) throws ServletException, IOException {

//        "https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50"

        // 判断极验验证码
        ResponseResult responseResult = geetestVerifyService.secondValidate(userValidModel.getChallenge(),userValidModel.getValidate(),userValidModel.getSeccode(),userValidModel.getGtServerStatus());
        if (responseResult.getCode() != 1) {
            log.error("极验验证失败");
            throw new CustomUnauthorizedException("验证码校验失败，请重试.");
        }

        User u1=userService.getOne(new QueryWrapper<User>().lambda().eq(User::getPhone, userValidModel.getUser().getPhone()));

        if (u1 == null) {
            throw new CustomUnauthorizedException("该账号不存在.");
        }
        // 对密码进行加密
        String key = passwordEncrypterUtil.encryptPassword(u1.getSalt(), userValidModel.getUser().getPassword());

        // 密码加密时用 用 随机盐+密码 组合加密，且密码不可解密
        if (key.equals(u1.getPassword())){
            // 清除可能存在的Shiro权限信息缓存
            if (JedisUtil.exists(Constant.PREFIX_SHIRO_CACHE + u1.getUsername())) {
                JedisUtil.delKey(Constant.PREFIX_SHIRO_CACHE + u1.getUsername());
            }

            // 设置RefreshToken，时间戳为当前时间戳，直接设置即可(不用先删后设，会覆盖已有的RefreshToken)
            String currentTimeMillis = String.valueOf(System.currentTimeMillis());

            JedisUtil.setObject(Constant.PREFIX_SHIRO_REFRESH_TOKEN + u1.getUsername(), currentTimeMillis, Integer.parseInt(refreshTokenExpireTime.trim()));

            // 从Header中Authorization返回AccessToken，时间戳为当前时间戳
            String token = JwtUtil.sign(u1.getUsername(), currentTimeMillis);
            response.setHeader("Authorization",token);
            response.setHeader("Access-Control-Expose-Headers","Authorization");
            response.setHeader("Cache-Control", "no-store");

            return new ResponseResult(HttpStatus.OK.value(), "登陆成功.",null);

        }else {
            throw new CustomUnauthorizedException("账号或密码错误.");
        }


    }

    /**
     * 获取当前登录用户信息
     * @param
     * @return com.wang.model.common.ResponseBean
     * @author dolyw.com
     * @date 2019/3/15 11:51
     */
    @ApiOperation(value = "获取用户信息", notes = "登陆成功后获取用户信息")
    @GetMapping("/profile")
    @RequiresAuthentication
    public ResponseResult getUserInfo() {
        // 获取当前登录用户
        User user = userUtil.getCurrentLoggedUser();

        UserResultModel userResultModel = new UserResultModel();

        userResultModel.setId(user.getId());
        userResultModel.setUsername(user.getUsername());
        userResultModel.setIdCardType(user.getIdCardType());
        userResultModel.setIdCard(user.getIdCard());
        userResultModel.setRealName(user.getRealName());
        userResultModel.setEmail(user.getEmail());
        userResultModel.setSex(user.getSex());
        userResultModel.setProvince(user.getProvince());
        userResultModel.setCity(user.getCity());
        userResultModel.setDistrict(user.getDistrict());
        userResultModel.setAddress(user.getAddress());
        userResultModel.setSignUpTime(user.getSignUpTime());
        userResultModel.setBirthday(user.getBirthday());
//        userResultModel.setIntro(user.getIntro());
        userResultModel.setPhone(user.getPhone());
        userResultModel.setAvatar(user.getAvatar());

        return new ResponseResult(HttpStatus.OK.value(), "您已经登录了.", userResultModel);
    }




    /**
     * 获取指定用户
     * @param id
     * @return java.util.Map<java.lang.String,java.lang.Object>
     * @author dolyw.com
     * @date 2018/8/30 10:42
     */
    @ApiOperation(value = "获取指定用户", notes = "获取指定用户")
    @GetMapping("/{id}")
    @RequiresPermissions(logical = Logical.AND, value = {"user:view"})
    public ResponseResult findById(@PathVariable("id") Integer id) {
        User user = userService.getById(id);
        if (user == null) {
            throw new CustomException("查询失败.");
        }
        return new ResponseResult(HttpStatus.OK.value(), "查询成功.", user);
    }

    /**
     * 验证用户名是否已经被注册了,返回是否可以注册
     * @param username
     * @return
     */
    @ApiOperation(value = "验证用户名", notes = "验证用户名是否已经被注册了,返回是否可以注册")
    @GetMapping("/verify/u/{username}")
    @RequiresGuest
    public ResponseResult verifyUsername(@PathVariable(required = false,name = "username") String username) {
        User user = userService.getUserByUsername(username);
        if (user != null && StringUtils.isNotBlank(user.getPassword())) {
            return new ResponseResult(HttpStatus.OK.value(), "用户名已存在.", false);
        }else {
            return new ResponseResult(HttpStatus.OK.value(), "用户名可以注册.", true);
        }
    }

    /**
     * 验证邮箱地址是否已经存在,返回是否可以注册
     * @param email
     * @return
     */
    @ApiOperation(value = "验证邮箱", notes = "验证邮箱地址是否已经存在,返回是否可以注册")
    @GetMapping("/verify/e/{email}")
    @RequiresGuest
    public ResponseResult verifyEmail(@PathVariable(required = false,name = "email") String email){
        User user = userService.getUserByEmail(email);
        if (user != null && StringUtils.isNotBlank(user.getPassword())) {
            return new ResponseResult(HttpStatus.OK.value(), "邮箱地址已存在.", false);
        }else {
            return new ResponseResult(HttpStatus.OK.value(), "邮箱地址可以注册.", true);
        }
    }

    /**
     * 验证手机号是否已经存在,返回是否可以注册
     * @param phone
     * @return
     */
    @ApiOperation(value = "验证手机号", notes = "验证手机号是否已经存在,返回是否可以注册")
    @GetMapping("/verify/p/{phone}")
    @RequiresGuest
    public ResponseResult verifyPhone(@PathVariable(required = false,name = "phone") String phone){
        User user = userService.getUserByPhone(phone);
        if (user != null && StringUtils.isNotBlank(user.getPassword())) {
            return new ResponseResult(HttpStatus.OK.value(), "手机号已存在.", false);
        }else {
            return new ResponseResult(HttpStatus.OK.value(), "手机号可以注册.", true);
        }
    }


    /**
     * 新增用户
     * @param userValidModel
     * @return java.util.Map<java.lang.String,java.lang.Object>
     * @author dolyw.com
     * @date 2018/8/30 10:42
     */
    @ApiOperation(value = "注册", notes = "用户注册")
    @PostMapping("/register")
    @ResponseBody
    @RequiresGuest
    public ResponseResult add(@RequestBody @Valid UserValidModel userValidModel) throws IOException {
        // 判断极验验证码
        ResponseResult responseResult = geetestVerifyService.secondValidate(userValidModel.getChallenge(), userValidModel.getValidate(), userValidModel.getSeccode(), userValidModel.getGtServerStatus());
        if (responseResult.getCode() != 1) {
            log.error("极验验证失败");
            return responseResult;
        }

        // 判断当前帐号是否存在(用户名、邮箱、手机号唯一)

        userValidModel.getUser().setGmtTime(new Date());
        // 判断密码长度是否满足设定
        if (userValidModel.getUser().getPassword().length() > Constant.PASSWORD_MAX_LEN) {
            throw new CustomException("密码最多20位.");
        }

        // 密码加密
        String salt = randomNumberGenerator.nextBytes().toHex();

        String encrPwd = passwordEncrypterUtil.encryptPassword(salt, userValidModel.getUser().getPassword());

        userValidModel.getUser().setSalt(salt);
        userValidModel.getUser().setPassword(encrPwd);

        // 默认头像生成
        String avatar = AvatarUtil.createBase64Avatar(Math.abs(userValidModel.getUser().getUsername().hashCode()));
        userValidModel.getUser().setAvatar(AvatarUtil.BASE64_PREFIX + avatar);

        // 新增加用户
        boolean res = userService.save(userValidModel.getUser());
        if (!res) {
            throw new CustomException("新增失败.");
        }
        // 登录成功，返回用户信息
        return new ResponseResult(HttpStatus.OK.value(), "新增成功.", userValidModel.getUser());
    }

    /**
     * 更新用户
     * @param user
     * @return java.util.Map<java.lang.String,java.lang.Object>
     * @author dolyw.com
     * @date 2018/8/30 10:42
     */
    @ApiOperation(value = "更新用户")
    @PutMapping
    @RequiresPermissions(logical = Logical.AND, value = {"user:edit"})
    public ResponseResult update(@RequestBody @Valid User user) {
        // 查询数据库密码
        User userTemp = new User();
        userTemp.setUsername(user.getUsername());

        QueryWrapper<User> queryWrapper = new QueryWrapper<>(userTemp);
        userTemp = userService.getOne(queryWrapper);
        if (userTemp == null) {
            throw new CustomUnauthorizedException("该帐号不存在.");
        } else {
            user.setId(userTemp.getId());
        }
        // FIXME: 如果不一样就说明用户修改了密码，重新加密密码(这个处理不太好，但是没有想到好的处理方式)
        if (!userTemp.getPassword().equals(user.getPassword())) {
            // 密码以帐号+密码的形式进行AES加密
            if (userTemp.getPassword().length() > Constant.PASSWORD_MAX_LEN) {
                throw new CustomException("密码最多8位.");
            }
            String key = AesCipherUtil.enCrypto(user.getUsername() + user.getPassword());
            user.setPassword(key);
        }

        boolean res = userService.updateById(user);
        if (!res) {
            throw new CustomException("更新失败.");
        }
        return new ResponseResult(HttpStatus.OK.value(), "更新成功.", user);
    }

    /**
     * 删除用户
     * @param id
     * @return java.util.Map<java.lang.String,java.lang.Object>
     * @author dolyw.com
     * @date 2018/8/30 10:43
     */
    @ApiOperation(value = "删除用户")
    @DeleteMapping("/{id}")
    @RequiresPermissions(logical = Logical.AND, value = {"user:edit"})
    public ResponseResult delete(@PathVariable("id") Integer id) {
        boolean res = userService.removeById(id);
        if (!res) {
            throw new CustomException("删除失败，ID不存在.");
        }
        return new ResponseResult(HttpStatus.OK.value(), "删除成功.", null);
    }

    /**
     * 剔除在线用户
     * @param id
     * @return com.wang.model.common.ResponseBean
     * @author dolyw.com
     * @date 2018/9/6 10:20
     */
    @ApiOperation(value = "剔除在线用户", notes = "删除redis中某个人的token来实现")
    @DeleteMapping("/delete/online/{id}")
    @RequiresPermissions(logical = Logical.AND, value = {"user:edit"})
    public ResponseResult deleteOnline(@PathVariable("id") Integer id) {
        User user = userService.getById(id);
        if (JedisUtil.exists(Constant.PREFIX_SHIRO_REFRESH_TOKEN + user.getUsername())) {
            if (JedisUtil.delKey(Constant.PREFIX_SHIRO_REFRESH_TOKEN + user.getUsername()) > 0) {
                return new ResponseResult(HttpStatus.OK.value(), "剔除成功.", null);
            }
        }
        throw new CustomException("剔除失败，Username不存在.");
    }

    /**
     * 生成头像图片
     * @return
     * username
     */
    @ApiOperation(value = "获取头像", notes = "通过传入的用户名返回一个默认的头像")
    @ApiImplicitParams({
            @ApiImplicitParam(name="username",value="手机号",required=true,paramType="query",dataType="String"),
    })
    @ApiResponses({
            @ApiResponse(code = 400, message = "请求参数异常"),
            @ApiResponse(code = 401, message = "无权限")
    })
    @GetMapping("/avatar")
    @ResponseBody
    public String GenerateAvatar(String username) throws IOException {
        String avatar = AvatarUtil.createBase64Avatar(Math.abs(username.hashCode()));

        log.debug("avatar :" + avatar);

        return AvatarUtil.BASE64_PREFIX + avatar;
    }
}
